package com.geek.javase.ch18.videodemo.pattern.proxy.cglib;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 *  * 一.Cglib代理
 *  * 在内存中构建一个子类对象从而实现对目标对象功能扩展
 *  *
 *  * 二.Cglib代理的优缺点
 *  * 优点：
 *  *      1.目标对象可以不实现接口
 *  *      2.可以在不改变原有的目标方法的基础上，实现对其增强功能
 *  *      3.实现解耦，实现代码的拓展性
 *  * 缺点：
 *  *      实现MethodInterceptor接口，重写拦截器intercept()
 *  *
 *  * 三.Cglib代理模式实现步骤
 *  * 1.引入jar包  Add As Library
 *  * 2.代理类中实现MethodInterceptor接口，重写拦截器intercept()
 *  * 3.提供一个获得代理对象的方法
 *  *
 *  * 四.特殊点
 *  * 1.必须在目标类中，提供一个公开的无参的构造方法
 *  * IllegalArgumentException: Superclass has no null constructors but no arguments were given
 *  * 2.不可以将当前目标类，设置为final，将无法在内存中构建子类对象
 *  * IllegalArgumentException: Cannot subclass final class class com.igeek.javase.pattern.proxy.cglib.BankService
 *  * 3.不可以将目标类中的方法，使用final修饰，这样会导致拦截器中的intercept()失效
 *  * 4.不可以将目标类中的方法，使用static修饰，这样会导致拦截器中的intercept()失效
 */
public class CglibProxyBankLogService implements MethodInterceptor {
    //目标对象
    private BankService target;

    public CglibProxyBankLogService(BankService target) {
        this.target = target;
    }
    //获取代理对象
    /*就是利用enhancer在内存中构建一个BankService的子类对象，
    *但是又想让创建出来的代理对象能够执行拦截器的
    * */
    public BankService getProxy(){
        //创建Enhancer实例
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(BankService.class);
        //设置回调 this就是MethodInterceptor拦截器，从而让intercept()方法生效
        enhancer.setCallback(this);
        //构建代理对象
        BankService proxy =(BankService)enhancer.create();
        return proxy;
    }



    /**
     * 拦截器，目的拦截目标方法，实现非业务逻辑（日志追踪、耗时统计...）
     * @param proxy    代理对象（一般不用）
     * @param method   目标方法对象
     * @param args     目标方法的形参列表
     * @param methodProxy  方法代理对象（一般不用）
     * @return 目标方法执行的结果，若目标方法没有返回值则为null
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy , Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        System.out.println("日志开始追踪:The Method " + methodName + " begin with[" + Arrays.toString(args) + "]");
        long l1 = System.currentTimeMillis();
        //执行目标方法 第一个参数是目标对象，第二个参数是方法的形参列表
        //返回值，目标方法的返回值，若目标方法没有返回值，则返回值为null
        Object result = method.invoke(target, args);
        long l2 = System.currentTimeMillis();
        System.out.println("日志追踪结束:The Method " + methodName + " end with " + result + " 耗时：" + (l2 - l1) + "ms");
        return result;
    }
}
